//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/net/chttprequest.h>
#include <buola/net/cxmlrpccall.h>
#include <buola/net/cxmlrpcreply.h>
#include <buola/app/ccmdline.h>
#include <buola/algorithm/base64.h>
#include <buola/algorithm/gzip.h>
#include <buola/io.h>
#include <numeric>

using namespace buola;

CCmdLineOption<std::string> sLangOption("lang",L"language code","eng");

std::string get_token()
{
    io::CXMLRPCCall lCall("LogIn");
    lCall.AddParam("");
    lCall.AddParam("");
    lCall.AddParam("");
    lCall.AddParam("OS Test User Agent");
    
    io::CHTTPRequest lRequest(io::uri("http://api.opensubtitles.org/xml-rpc"));
    lRequest.SetPost(lCall.XML());
    std::string lReply;
    lRequest.Perform(lReply);
    
    io::CXMLRPCReply lResponse(lReply);
    
    io::CXMLRPCStruct lStruct;
    lResponse.GetValue(lStruct);
    std::string lToken;
    lStruct.GetItem("token",lToken);
    return lToken;
}

std::pair<uint64_t,std::string> calc_hash(const std::string &pFileName)
{
    io::PStream lStream=io::open(io::file(pFileName),io::EMode::READ);
    
    uint64_t lBuffer[16384];
    lStream->Seek(0,io::ESeek::ABSOLUTE);
    lStream->Read((uint8_t*)lBuffer,65536);
    lStream->Seek(-65536,io::ESeek::END);
    lStream->Read((uint8_t*)(lBuffer+8192),65536);
    
    uint64_t lHash=std::accumulate(lBuffer,lBuffer+16384,(uint64_t)lStream->Tell());
    return std::make_pair(lStream->Tell(),hex_string(lHash));
}

std::string search_subtitles(const std::string &pToken,uint64_t pSize,const std::string &pHash)
{
    io::CXMLRPCCall lCall("SearchSubtitles");
    lCall.AddParam(pToken);
    io::CXMLRPCArray lArray(lCall);
    io::CXMLRPCStruct lStruct(lCall);
    lStruct.AddItem("sublanguageid",*sLangOption);
    lStruct.AddItem("moviehash",pHash);
    lStruct.AddItem("moviebytesize",(double)pSize);
    lArray.Add(lStruct);
    lCall.AddParam(lArray);
    
    io::CHTTPRequest lRequest(io::uri("http://api.opensubtitles.org/xml-rpc"));
    lRequest.SetPost(lCall.XML());
    std::string lReply;
    lRequest.Perform(lReply);

    msg_info() << lReply << "\n";

    io::CXMLRPCReply lResponse(lReply);
    
    io::CXMLRPCStruct lRStruct;
    lResponse.GetValue(lRStruct);
    
    io::CXMLRPCArray lEntries;
    lRStruct.GetItem("data",lEntries);
    
    for(int i=0;i<lEntries.GetCount();i++)
    {
        io::CXMLRPCStruct lEntry;
        lEntries.Get(i,lEntry);
        
        std::string lIdSubtitleFile;
        lEntry.GetItem("IDSubtitleFile",lIdSubtitleFile);
        return lIdSubtitleFile;
    }
    
    throw XNotFound("no subtitles found");
}

std::vector<uint8_t> download_subtitle(const std::string &pToken,const std::string &pID)
{
    io::CXMLRPCCall lCall("DownloadSubtitles");
    lCall.AddParam(pToken);
    io::CXMLRPCArray lArray(lCall);
    lArray.Add(pID);
    lCall.AddParam(lArray);
    
    io::CHTTPRequest lRequest(io::uri("http://api.opensubtitles.org/xml-rpc"));
    lRequest.SetPost(lCall.XML());
    std::string lReply;
    lRequest.Perform(lReply);
    
    io::CXMLRPCReply lResponse(lReply);
    
    io::CXMLRPCStruct lRStruct;
    lResponse.GetValue(lRStruct);
    
    io::CXMLRPCArray lEntries;
    lRStruct.GetItem("data",lEntries);
    
    io::CXMLRPCStruct lEntry;
    lEntries.Get(0,lEntry);
    
    std::string lData;
    lEntry.GetItem("data",lData);
    
    std::vector<uint8_t> lDecoded=base64_decode(lData);
    std::vector<uint8_t> lDecompressed=gunzip_buffer(lDecoded);
    return lDecompressed;
}

int main(int pNArg,char **pArgs)
{
    buola_init(pNArg,pArgs);

    if(cmd_line().ArgCount()<1)
    {
        msg_info() << "usage: subs filenames\n";
        return 1;
    }
    
    for(int i=0;i<cmd_line().ArgCount();i++)
    {
        try
        {
            uint64_t lSize;
            std::string lHash;
            std::tie(lSize,lHash)=calc_hash(cmd_line().Arg(i));
            std::string lToken=get_token();
            std::string lIdSubtitleFile=search_subtitles(lToken,lSize,lHash);
            std::vector<uint8_t> lSubtitle=download_subtitle(lToken,lIdSubtitleFile);
            
            io::CURI lURI=io::file(cmd_line().Arg(i));
            lURI.SetExtension("srt");
            io::PStream lOStream=io::open(lURI,io::EMode::WRITE);
            lOStream->Write(lSubtitle.data(),lSubtitle.size());
            
            msg_info() << "downloaded " << lURI << "\n";
        }
        catch(std::exception &pE)
        {
            msg_info() << "caught exception " << pE.what() << "downloading subtitle for " << cmd_line().Arg(i) << "\n";
        }
    }
    
    return buola_finish();
}
